#include "TcpClient.h"
namespace mpgl_engine{ namespace application{ namespace net {


	/*---------------------------------------------------
	------------------------------------------------------*/

	TcpClient::TcpClient(GlConsole* console, io_service* ioService) :
		m_connectionMode(TcpClientConnectionMode::DISCONNECTED),
		
		TcpSession(console, ioService, TcpSessionType::CLIENT)
	{
#if (TCP_SESSION_DEBUG >= 1)
		if (m_console != NULL){
			string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
			msg += " TcpClient :: Constructed ";
			m_console->WriteMessage(msg);
		}
#endif
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

	TcpClient::~TcpClient()
	{
		while (!TryLock()){ Pause(1); }
		m_console = NULL;
		Disconnect(); ClearResolver(); Unlock();
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

	void TcpClient::Connect()
	{
		if (m_resolver != NULL && 
			m_connectionMode != 
				TcpClientConnectionMode::CONNECTED)
		{
			//Set connecting flag to true
			m_connectionMode = 
				TcpClientConnectionMode::CONNECTING;
			
			//Create a query for a server on localhost.
			tcp::resolver::query q = 
				tcp::resolver::query
				(
					"127.0.0.1", 
					boost::lexical_cast<string>
					(
						NetworkDefines::TCP_DEF_PORT
					)
				);

			//Connect to the query results asynchronously.
			m_resolver->async_resolve
				(
					(q),
					boost::bind(
					&TcpClient::HandleResolve,
					this,
					this,
					boost::asio::placeholders::error,
					boost::asio::placeholders::iterator
				)
			);

			//Print to console stating that an async connection has
			// started.
#if TCP_SESSION_DEBUG
			if (m_console != NULL){
				string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
				msg += "TcpClient :: Async Connection Started";
				m_console->WriteMessage(msg);
			}
#endif			
		}
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

	void TcpClient::Update()
	{
		//Retry connection
		if ( m_connectionMode == 
			 TcpClientConnectionMode::DISCONNECTED &&
			 !m_stopRequest )
		{
#if TCP_SESSION_DEBUG
			if (m_console != NULL){
				string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
				msg += "TcpClient :: Retry in 3 seconds";
				m_console->WriteMessage(msg);
			}
#endif	
			Sleep(3000);
			if (!m_stopRequest)
			{
				Connect();
			}
		}
		//Update base session, and connections
		TcpSession::Update();
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

	void TcpClient::HandleResolve(
		TcpClient* client,
		const boost::system::error_code& error,
		tcp::resolver::iterator endpoint_iterator)
	{
		bool connected = false;
		if (!error)
		{

#if TCP_SESSION_DEBUG
			if (m_console != NULL){
				string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
				msg += "TcpClient :: Async Connection Results";
				m_console->WriteMessage(msg);
			}
#endif	
			//Create new connection
			while (!TryLock()){ Pause(1); }

			TcpConnection* con = NULL;
			try{
				/*---------------------------------
				Normal Operation
				----------------------------------*/

				con = new TcpConnection(*m_ioService);
				//Connect new connection to endpoint
				con->Connect(endpoint_iterator);

#if TCP_SESSION_DEBUG
				if (m_console != NULL){
					string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
					msg += "TcpClient :: Async Connection Successful";
					m_console->WriteMessage(msg);
				}
#endif
				//Set connecting flag to true
				connected = true;
			}
			catch (string error)
			{
				/*---------------------------------
				On Fail
				----------------------------------*/
#if TCP_SESSION_DEBUG
				if (m_console != NULL){
					string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
					msg += "TcpClient :: Async Connection Failed -";
					msg += error;
					m_console->WriteMessage(msg);
				}
#endif		
				if (con != NULL){delete con;}
			}
			catch (...)
			{
				if (m_console != NULL){
					string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
					msg += "TcpClient :: Async Connection Failed";
					m_console->WriteMessage(msg);
				}
				if (con != NULL){ delete con; }
			}

			//If connected OK after Try block
			if (connected)
			{
				//Add the new connection
				m_connections.push_back(con);
			}

			//Unlock
			Unlock();

		}else{
			/*---------------------------------
			On boost Error
			----------------------------------*/
#if TCP_SESSION_DEBUG
			if (m_console != NULL){
				string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
				msg += "TcpClient :: Async Connection Error";
				m_console->WriteMessage(msg);
			}
#endif		
		}//End of If !error
		if (connected)
		{
			m_connectionMode =
				TcpClientConnectionMode::CONNECTED;
		}
		else
		{
			m_connectionMode =
				TcpClientConnectionMode::DISCONNECTED;
		}
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

	void TcpClient::Disconnect()
	{
#if TCP_SESSION_DEBUG
		if (m_console != NULL){
			string msg = NetworkDefines::CONSOLE_MSG_PREFIX;
			msg += "TcpClient :: Disconnecting";
			m_console->WriteMessage(msg);
		}
#endif	
		//If we are connected or trying to connect
		if (m_connectionMode ==
			TcpClientConnectionMode::CONNECTING || 
			m_connectionMode ==
			TcpClientConnectionMode::CONNECTED)
		{
			//Request complete shutdown
			m_stopRequest = true;

			//Wait for jobs to complete
			while (m_connectionMode ==
				TcpClientConnectionMode::CONNECTING ||
				m_connectionMode ==
				TcpClientConnectionMode::CONNECTED)
				{Pause(1);} 

			//Lock
			while (!TryLock()){ Pause(1); }

			//Delete connections
			if (m_connections.size() > 0){
				TcpConnection* con;
				con = *m_connections.end();
				m_connections.pop_back();
				con->Disconnect();
				delete con;
			}

			//Unlock
			Unlock();
		}
	}

	/*---------------------------------------------------
	------------------------------------------------------*/

}}}